library(tidyverse)
library(infer)
library(mosaic)
library(caret)
library(ggplot2)
library(mdsr)
library(rpart)
library(rpart.plot)
library(partykit)
library(randomForest)
library(pROC)
library(gbm)
library(Metrics)
library(viridis)
require(maps)
require(viridis)
theme_set(
  theme_void()
  )
library(knitr)
library(tmap)
library(ggpubr)

The Premise

Hello! This project is the final for my STAT228: Introduction to Data Science course at Simmons University. With this project, I’d like to encompass everything I’ve learned throughout the semester, as well as some additional data science methods that I have taught myself outside of class. (The primary non-curriculum methods I am using come from the package tmap, which I was made aware of from a LinkedIn post by Joachim Schork, a data science educator & consultant from Germany). The premise of my project is to predict & analyze Women’s Empowerment Index scores for countries ; in this project, I aim to find the best version of the model predicting WEI scores using a variety of ensemble methods. There are two datasets I’m interested in using here:

I’d like to join the two datasets by the common variable “Country”, and analyze WEI scores by health factors related to the patient’s country. I am interested in creating several maps that will visualize WEI scores against other health-based factors.

Cleaning the Data

I want to start by filtering led to only include data where the year = 2015, because this is the most recent year within this dataset.

led_2015 <-     led%>%
    filter(Year=="2015")

Now, I want to rename several datapoints within the led_2015 dataset because I want the data names to be congruent between led_2015 and wei. This will be very tedious, but it is necessary to be able to join the datasets!

led_2015[led_2015$Country == "Bolivia(PlurinationalStateof)", "Country"] <- "Bolivia"

wei[wei$Country == "Bolivia(Plurinational State of)", "Country"] <- "Bolivia"

led_2015[led_2015$Country == "BosniaandHerzegovina", "Country"] <- "Bosnia and Herzegovina"

led_2015[led_2015$Country == "BurkinaFaso", "Country"] <- "Burkina Faso"

wei[wei$Country == "Congo (Democratic Republic of the)", "Country"] <- "DemocraticRepublicoftheCongo"

led_2015[led_2015$Country == "CostaRica", "Country"] <- "Costa Rica"

led_2015[led_2015$Country == "DominicanRepublic", "Country"] <- "Dominican Republic"

led_2015[led_2015$Country == "ElSalvador", "Country"] <- "El Salvador"

led_2015[led_2015$Country == "Iran(IslamicRepublicof)", "Country"] <- "Iran (Islamic Republic of)"

led_2015[led_2015$Country == "LaoPeople'sDemocraticRepublic", "Country"] <- "Laos"

wei[wei$Country == "Lao People's Democratic Republic", "Country"] <- "Laos"

led_2015[led_2015$Country == "RepublicofMoldova", "Country"] <- "Moldova (Republic of)"

led_2015[led_2015$Country == "SierraLeone", "Country"] <- "Sierra Leone"

led_2015[led_2015$Country == "SouthAfrica", "Country"] <- "South Africa"

led_2015[led_2015$Country == "SriLanka", "Country"] <- "Sri Lanka"

led_2015[led_2015$Country == "TheformerYugoslavrepublicofMacedonia", "Country"] <- "North Macedonia"

led_2015[led_2015$Country == "UnitedRepublicofTanzania", "Country"] <- "Tanzania (United Republic of)"

wei[wei$Country == "Türkiye", "Country"] <- "Turkey"

led_2015[led_2015$Country == "UnitedArabEmirates", "Country"] <- "United Arab Emirates"

led_2015[led_2015$Country == "UnitedKingdomofGreatBritainandNorthernIreland", "Country"] <- "United Kingdom"

led_2015[led_2015$Country == "UnitedStatesofAmerica", "Country"] <- "United States"

led_2015[led_2015$Country == "VietNam", "Country"] <- "Viet Nam"

That took a WHILE! But, now my two datasets should be better matched, and it’ll be much easier to join them!

Now I’m going to remove some unnecessary variables from the datasets This is because some variables have too many missing datapoints to be useful, or because some variables may be redundant.

led_2015 = subset(led_2015, select = -c(Alcohol, Totalexpenditure,Year))

Now, I will be joining the two datasets using inner_join, because I only want the countries in common between both dataframes. I’m also going to create a second version that is full_joined, just in case I end up needing it!

dataset <- wei%>% inner_join(led_2015,by="Country")

dataset2 <- wei%>% full_join(led_2015,by="Country")
dim(dataset)
## [1] 112  25

Finally, I will be renaming a few variables because I dislike having spaces in my variable names. After this, I will have finished cleaning the data! I’m also removing a few more variables just because they seem rather redundant - for example, the Gender Parity Group and Gender Parity Index variables may be too similar to WEI, therefore they might have skewed levels of correlation.

dataset <- dataset %>%
  rename(WEI = "Women's Empowerment Index (WEI) - 2022")

dataset <- dataset %>%
  rename(WEG = "Women's Empowerment Group - 2022")

dataset <- dataset %>%
  rename(GGPI = "Global Gender Parity Index (GGPI) - 2022")

dataset <- dataset %>%
  rename(HDG = "Human Development Group - 2021")

dataset <- dataset %>%
  rename(GPG = "Gender Parity Group - 2022")

dataset <- dataset %>%
  rename(SDG_Region = "Sustainable Development Goal regions")
dataset = subset(dataset, select = -c(GPG, SDG_Region,GGPI))

All finished cleaning! Now I want to take a peek at the dataset I’ll be working with before I go into the next steps.

dim(dataset)
## [1] 112  22
summary(dataset$WEI)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.1410  0.5180  0.6150  0.6085  0.7070  0.8280
tally(dataset$Status)
## X
##  Developed Developing 
##         31         81

Intial Visualization

Now, I want to create a map of WEI by country, just to get a general visual of how the distribution looks.

I’m going to start out by creating a joined dataset between dataset and World, so I can have a mappable dataset using the tmap package.

library(tmap)
data("World")

dataset<- dataset %>%
  rename(name = Country)

wei.map <- inner_join(dataset,World,by="name")

Unfortunately, the wei.map only contains 98 individuals despite dataset containing 112 individuals! This means that 14 individuals have disharmonious names between dataset and World. I’m going to run some code renaming a whole bunch of countries, just like I did above with the led_2015 dataset. I’m going to hide this code, just because it’s super tedious work. Tidying & cleaning your data is a constant process!

dataset[dataset$name == "Bosnia and Herzegovina", "name"] <- "Bosnia and Herz."

dataset[dataset$name == "DemocraticRepublicoftheCongo", "name"] <- "Dem. Rep. Congo"

dataset[dataset$name == "Dominican Republic", "name"] <- "Dominican Rep."

dataset[dataset$name == "Iran (Islamic Republic of)", "name"] <- "Iran"

dataset[dataset$name == "Laos", "name"] <- "Lao PDR"

dataset[dataset$name == "North Macedonia", "name"] <- "Macedonia"

dataset[dataset$name == "Moldova (Republic of)", "name"] <- "Moldova"

dataset[dataset$name == "Tanzania (United Republic of)", "name"] <- "Tanzania"

dataset[dataset$name == "Viet Nam", "name"] <- "Vietnam"

Now, let’s try re-joining these datasets!

wei.map <- right_join(dataset,World,by="name")
library(s2)
library(sf)
## Warning: package 'sf' was built under R version 4.3.2
wei.map.sf  <- wei.map %>% 
  st_as_sf()

And visualizing:

tmap_mode("plot")
tm_shape(wei.map.sf) +
    tm_polygons("WEI",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Women's Empowerment Index Scores", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

Lovely! I also made an interactive version of the map, just for further exploration of the tmap package.

tmap_mode("view")
tm_shape(wei.map.sf) +
    tm_polygons("WEI",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Women's Empowerment Index Scores", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

Now, I want to move onto creating models that will help me predict the WEI scores of countries based on other factors.

Decision Tree

I’m going to start by creating a decision tree for my dataset. I believe this will be beneficial to see, seeing as the decision tree will select the best predictors for my response variable. The first step here is to split my data into training and testing data.

set.seed(228)
train.index <- createDataPartition(dataset$WEI, p= 0.8, list = FALSE)
train <- dataset[train.index,]
test <- dataset[-train.index,]

I’m going to look at the proportion of developed vs developing countries within both my test and train data. This is so I can ensure that test is at least somewhat representative of the distribution within train.

tally(test$Status, format='proportion')
## X
##  Developed Developing 
##        0.3        0.7
tally(train$Status, format = 'proportion')
## X
##  Developed Developing 
##  0.2717391  0.7282609
tally(dataset$Status, format = 'proportion')
## X
##  Developed Developing 
##  0.2767857  0.7232143

Both test and train appear to be adequately representative of dataset!

Now, I will create my decision tree. I will fit the decision tree to train, and I will set nearly every predictor variable (BESIDES NAME & WEG, for reasons of redundancy) as my X. I am doing this so that the decision tree can tell me which predictor variables have the highest influence on my response variable, WEI.

tree <- rpart(WEI~ HDG + Status + Lifeexpectancy + AdultMortality + infantdeaths + HepatitisB + Measles +  Polio + Diphtheria + Population + Schooling, data=train, cp=.01)

rpart.plot(tree)

It appears there are three predictors that have the highest correlation with WEI scores : HDG (Human Development Group), Years of Schooling, and Adult Mortality Rates. For further exploration, I’m going to create a Random Forest to find our best model.

Random Forest & RMSE

I’m going to have to not use any variables that have “NA” values to be able to properly crearte a random forest. This unfortunately means removing Schooling. However, I don’t want to remove Schooling because according to the decision tree, it’s one of the most important predictors! So, I am going to use regression imputation to create some estimated Schooling values for any of our countries that have Schooling = NA.

missing <- is.na(dataset$Schooling) 

sum(missing)
## [1] 6
which(missing)
## [1]  11  30  31  47  72 105
lm(Schooling~WEI, data=dataset)
## 
## Call:
## lm(formula = Schooling ~ WEI, data = dataset)
## 
## Coefficients:
## (Intercept)          WEI  
##       2.574       17.943
dataset$Schooling[11]<-2.574+17.943*(0.707)
dataset$Schooling[30]<-2.574+17.943*(0.778)
dataset$Schooling[31]<-2.574+17.943*(0.752)
dataset$Schooling[47]<-2.574+17.943*(0.686)
dataset$Schooling[72]<-2.574+17.943*(0.399)
dataset$Schooling[105]<-2.574+17.943*(0.510)

Yet another example of never being done with cleaning the data! Now, I have to recreate my training and testing dataset without these missing values.

set.seed(228)
train.index <- createDataPartition(dataset$WEI, p= 0.8, list = FALSE)
train <- dataset[train.index,]
test <- dataset[-train.index,]

Then, I can finally create my Random Forest

set.seed(228)
rf_model <-randomForest(WEI ~ HDG + Status + Lifeexpectancy + Schooling + AdultMortality + infantdeaths + Measles +  Polio + Diphtheria ,data=train, proximity=TRUE, ntree=1000)
rf_model
## 
## Call:
##  randomForest(formula = WEI ~ HDG + Status + Lifeexpectancy +      Schooling + AdultMortality + infantdeaths + Measles + Polio +      Diphtheria, data = train, proximity = TRUE, ntree = 1000) 
##                Type of random forest: regression
##                      Number of trees: 1000
## No. of variables tried at each split: 3
## 
##           Mean of squared residuals: 0.005114937
##                     % Var explained: 72

The mean of squared residuals is pretty close to 0, which means the model is good! However, I’m going to also try running a Random Forest model in which I only use the three best predictor variables as identified by my Decision Tree. I will then compare the two mean of squared residuals.

set.seed(228)
rf_model2 <-randomForest(WEI ~ HDG +  Schooling + AdultMortality  ,data=train, proximity=TRUE, ntree=1000)
rf_model2
## 
## Call:
##  randomForest(formula = WEI ~ HDG + Schooling + AdultMortality,      data = train, proximity = TRUE, ntree = 1000) 
##                Type of random forest: regression
##                      Number of trees: 1000
## No. of variables tried at each split: 1
## 
##           Mean of squared residuals: 0.005898256
##                     % Var explained: 67.72

The mean of squared residuals is ever so slightly higher for this model, and the Var explained is slightly lower. I’m going to use an RMSE test to compare the two models.

Now, I’m going to boost the two models. Before doing so, I am changing HDG’s class to ordered, and Status’s class to ordered, so they arecompatible with the boosted model.

train$HDG <- as.ordered(train$HDG)
train$Status <- as.ordered(train$Status)
boost1 <- gbm(WEI ~ HDG + Status + Lifeexpectancy + Schooling + AdultMortality + infantdeaths + Measles +  Polio + Diphtheria, data=train,distribution = "gaussian", n.trees = 1000, shrinkage = 0.01, n.minobsinnode=5)
boost2 <- gbm(WEI~ Schooling + HDG + AdultMortality , data = train, distribution = "gaussian", n.trees = 1000, shrinkage = 0.01, n.minobsinnode=5)

Now I will evaluate the two models using predictions & RMSE.

predictions1 <- predict(boost1,newdata=test)
rmse(actual=test$WEI, predicted=predictions1)
## [1] 0.0567137
predictions2 <- predict(boost2, newdata = test)
    rmse(actual=test$WEI, predicted=predictions2)
## [1] 0.05870663

Once again, the model with nearly very variable has proved to perform slightly better than the model with only 3 predictor variables. However, this difference is so small, and I think it is worth it to sacrifice this small difference for the sake of keeping our model SIMPLE! The model still performs very well. So, I’m deciding to keep the model that only contains the three best predictor variables!

Final Visualizations

wei.map <- right_join(dataset,World,by="name")

wei.map.sf  <- wei.map %>% 
  st_as_sf()

I’m going to create FOUR maps here: One exactly like my first visualization that shows WEI scores by country, and then three that shows the distribution of each of my predictor variables! I will display all of these maps side by side.

tmap_mode("plot")

 tm_shape(wei.map.sf) + tm_polygons("WEI",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Women's Empowerment Index Scores", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

tm_shape(wei.map.sf) + tm_polygons("HDG",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Human Development Groups", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

tm_shape(wei.map.sf) + tm_polygons("Schooling",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Years of Schooling", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

tm_shape(wei.map.sf) + tm_polygons("AdultMortality",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Adult Mortality per Population of 1000", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

These are sort of confusing. For one, the HDG categories are out of order, which is very hard to read. Secondly, the adult mortality map should probably have the colors flipped, because lower adult mortality corresponds to higher WEI.

I’m going to fix them, and insert the png below.

knitr::include_graphics("/Users/jasmineday/Downloads/WEIPLOT.jpg")

And there we have our final visualization! I hope you enjoyed reading through this project as much as I enjoyed my STAT228 class :)

BONUS: Testing the Prediction Model

After finishing the first draft of my project, I figured I wanted to add a section in which I take a small handful of countries that are NOT represented in the wei dataset and use our model to predict the WEI scores for these nations. I will be using the countries Kazakhstan, New Zealand, Sudan, Haiti, and Ukraine. I will use led_2015 to source the HDG, Schooling, and AdultMortality for each of these countries.

finalmodel <- lm(WEI~HDG+Schooling+AdultMortality,data=dataset)
finalmodel
## 
## Call:
## lm(formula = WEI ~ HDG + Schooling + AdultMortality, data = dataset)
## 
## Coefficients:
##    (Intercept)          HDGLow       HDGMedium    HDGVery high       Schooling  
##      0.1862461      -0.0749196      -0.0202205       0.0717149       0.0282162  
## AdultMortality  
##      0.0002026
#Kazakhstan:
  #HDG=Very high
  #Schooling=15
  #AdultMortality=198
Kazakhstan_WEI <- .0186 + .0717 + .0282*15 + .0002*198
#New Zealand  
  #HDG=Very high
  #Schooling=19.2
  #AdultMortality=66
NZ_WEI <- .0186 + .0717 + .0282*19.2 + .0002*66
#Sudan
  #HDG=Low
  #Schooling=7.2
  #AdultMortality=225
Sudan_WEI <- .0186 - .0749 + .0282*7.2 + .0002*225
#Haiti
  #HDG=Medium
  #Schooling=9.1
  #AdultMortality=24
Haiti_WEI <- .0186 - .0202 + .0282*9.1 + .0002*24
#Ukraine
  #HDG=High
  #Schooling=15.3
  #AdultMortality=195
Ukraine_WEI <- .0186 + .0282*15.3 + .0002*195

Kazakhstan imputed/predicted WEI score = .553 New Zealand imputed/predicted WEI score = .645 Sudan imputed/predicted WEI score = .192 Haiti imputed/predicted WEI score = .260 Ukraine imputed/predicted WEI score = .489

These don’t feel entirely accurate. For example, Sudan’s imputed WEI score is just above that of Yemen’s, which is the lowest in the world. However, it still can be beneficial to see where there may be potential flaws in using this linear model to predict WEI scores! WEI score data on all countries is not public, so I am unfortunately unable to check the actual accuracy of these predictions.

LS0tCnRpdGxlOiAiRmluZGluZyB0aGUgQmVzdCBNb2RlbCBmb3IgV29tZW4ncyBFbXBvd2VybWVudCBJbmRleCBTY29yZXMiCmF1dGhvcjogIkphc21pbmUgRGF5IgpkYXRlOiAiTWF5IDZ0aCAyMDI0IgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShpbmZlcikKbGlicmFyeShtb3NhaWMpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShtZHNyKQpsaWJyYXJ5KHJwYXJ0KQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkocGFydHlraXQpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoZ2JtKQpsaWJyYXJ5KE1ldHJpY3MpCmxpYnJhcnkodmlyaWRpcykKcmVxdWlyZShtYXBzKQpyZXF1aXJlKHZpcmlkaXMpCnRoZW1lX3NldCgKICB0aGVtZV92b2lkKCkKICApCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodG1hcCkKbGlicmFyeShnZ3B1YnIpCgpgYGAKCmBgYHtyLCBlY2hvID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KHJlYWRyKQoKd2VpIDwtIHJlYWRfY3N2KCJEQVRBU0VUUy93b21lbl9lbXBvd2VybWVudF9pbmRleC5jc3YiKQoKbGVkIDwtIHJlYWRfY3N2KCJEQVRBU0VUUy9sZWQuY3N2IikKCmBgYAoKCiMgVGhlIFByZW1pc2UKSGVsbG8hIFRoaXMgcHJvamVjdCBpcyB0aGUgZmluYWwgZm9yIG15IFNUQVQyMjg6IEludHJvZHVjdGlvbiB0byBEYXRhIFNjaWVuY2UgY291cnNlIGF0IFNpbW1vbnMgVW5pdmVyc2l0eS4gV2l0aCB0aGlzIHByb2plY3QsIEknZCBsaWtlIHRvIGVuY29tcGFzcyBldmVyeXRoaW5nIEkndmUgbGVhcm5lZCB0aHJvdWdob3V0IHRoZSBzZW1lc3RlciwgYXMgd2VsbCBhcyBzb21lIGFkZGl0aW9uYWwgZGF0YSBzY2llbmNlIG1ldGhvZHMgdGhhdCBJIGhhdmUgdGF1Z2h0IG15c2VsZiBvdXRzaWRlIG9mIGNsYXNzLiAoVGhlIHByaW1hcnkgbm9uLWN1cnJpY3VsdW0gbWV0aG9kcyBJIGFtIHVzaW5nIGNvbWUgZnJvbSB0aGUgcGFja2FnZSAqKnRtYXAqKiwgd2hpY2ggSSB3YXMgbWFkZSBhd2FyZSBvZiBmcm9tIGEgTGlua2VkSW4gcG9zdCBieSBKb2FjaGltIFNjaG9yaywgYSBkYXRhIHNjaWVuY2UgZWR1Y2F0b3IgJiBjb25zdWx0YW50IGZyb20gR2VybWFueSkuIFRoZSBwcmVtaXNlIG9mIG15IHByb2plY3QgaXMgdG8gcHJlZGljdCAmIGFuYWx5emUgV29tZW4ncyBFbXBvd2VybWVudCBJbmRleCBzY29yZXMgZm9yIGNvdW50cmllcyA7IGluIHRoaXMgcHJvamVjdCwgSSBhaW0gdG8gZmluZCB0aGUgYmVzdCB2ZXJzaW9uIG9mIHRoZSBtb2RlbCBwcmVkaWN0aW5nIFdFSSBzY29yZXMgdXNpbmcgYSB2YXJpZXR5IG9mIGVuc2VtYmxlIG1ldGhvZHMuIFRoZXJlIGFyZSB0d28gZGF0YXNldHMgSSdtIGludGVyZXN0ZWQgaW4gdXNpbmcgaGVyZTogCgotIDEuIHRoZSAqd2VpKiBkYXRhc2V0LCB3aGljaCBzdGFuZHMgZm9yIFdvbWVuJ3MgRW1wb3dlcm1lbnQgSW5kZXgsIGlzIHRoZSBmaXJzdCBkYXRhc2V0IEknbSBnb2luZyB0byBiZSB1c2luZy4gSXQgaXMgc291cmNlZCBmcm9tIEh1bWFuIERldmVsb3BtZW50IFJlcG9ydHMsIGFuZCBjYW4gYmUgYWNjZXNzZWQgYXQgdGhlIGxpbmsgYmVsb3cuICgqKmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvaWFtc291cmF2YmFuZXJqZWUvd29tZW4tZW1wb3dlcm1lbnQtaW5kZXgqKikuIEl0IGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIHRoZSBXRUkgc2NvcmVzIGZvciAxMTQgY291bnRyaWVzLiAKLSAyLiBUaGUgc2Vjb25kIGRhdGFzZXQgSSdtIGludGVyZXN0ZWQgaW4gdXNpbmcgaXMgdGhlICpsZWQqIGRhdGFzZXQsIHdoaWNoIHN0YW5kcyBmb3IgTGlmZSBFeHB0ZWN0YW5jeSBEYXRhc2V0LCBhbmQgaXMgc291cmNlZCBmcm9tIHRoZSBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uLiBUaGlzIGRhdGFzZXQgY2FuIGJlIGFjY2Vzc2VkIGF0IHRoZSBsaW5rIGJlbG93LiAoKipodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2F1Z3VzdHVzMDQ5OC9saWZlLWV4cGVjdGFuY3ktd2hvL2RhdGEqKikuIFRoaXMgZGF0YXNldCBjb250YWlucyBpbXBvcnRhbnQgaGVhbHRoLWNlbnRlcmVkIGRhdGEgb24gZXZlcnkgY291bnRyeSBpbiB0aGUgd29ybGQuCgpJJ2QgbGlrZSB0byBqb2luIHRoZSB0d28gZGF0YXNldHMgYnkgdGhlIGNvbW1vbiB2YXJpYWJsZSAiQ291bnRyeSIsIGFuZCBhbmFseXplIFdFSSBzY29yZXMgYnkgaGVhbHRoIGZhY3RvcnMgcmVsYXRlZCB0byB0aGUgcGF0aWVudCdzIGNvdW50cnkuIEkgYW0gaW50ZXJlc3RlZCBpbiBjcmVhdGluZyBzZXZlcmFsIG1hcHMgdGhhdCB3aWxsIHZpc3VhbGl6ZSBXRUkgc2NvcmVzIGFnYWluc3Qgb3RoZXIgaGVhbHRoLWJhc2VkIGZhY3RvcnMuIAoKIyBDbGVhbmluZyB0aGUgRGF0YQpJIHdhbnQgdG8gc3RhcnQgYnkgZmlsdGVyaW5nICpsZWQqIHRvIG9ubHkgaW5jbHVkZSBkYXRhIHdoZXJlIHRoZSB5ZWFyID0gMjAxNSwgYmVjYXVzZSB0aGlzIGlzIHRoZSBtb3N0IHJlY2VudCB5ZWFyIHdpdGhpbiB0aGlzIGRhdGFzZXQuCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KCmxlZF8yMDE1IDwtIAlsZWQlPiUKCWZpbHRlcihZZWFyPT0iMjAxNSIpCgpgYGAKCk5vdywgSSB3YW50IHRvIHJlbmFtZSBzZXZlcmFsIGRhdGFwb2ludHMgd2l0aGluIHRoZSAqbGVkXzIwMTUqIGRhdGFzZXQgYmVjYXVzZSBJIHdhbnQgdGhlIGRhdGEgbmFtZXMgdG8gYmUgY29uZ3J1ZW50IGJldHdlZW4gKmxlZF8yMDE1KiBhbmQgKndlaSouIFRoaXMgd2lsbCBiZSB2ZXJ5IHRlZGlvdXMsIGJ1dCBpdCBpcyBuZWNlc3NhcnkgdG8gYmUgYWJsZSB0byBqb2luIHRoZSBkYXRhc2V0cyEgCmBgYHtyfQoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiQm9saXZpYShQbHVyaW5hdGlvbmFsU3RhdGVvZikiLCAiQ291bnRyeSJdIDwtICJCb2xpdmlhIgoKd2VpW3dlaSRDb3VudHJ5ID09ICJCb2xpdmlhKFBsdXJpbmF0aW9uYWwgU3RhdGUgb2YpIiwgIkNvdW50cnkiXSA8LSAiQm9saXZpYSIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIkJvc25pYWFuZEhlcnplZ292aW5hIiwgIkNvdW50cnkiXSA8LSAiQm9zbmlhIGFuZCBIZXJ6ZWdvdmluYSIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIkJ1cmtpbmFGYXNvIiwgIkNvdW50cnkiXSA8LSAiQnVya2luYSBGYXNvIgoKd2VpW3dlaSRDb3VudHJ5ID09ICJDb25nbyAoRGVtb2NyYXRpYyBSZXB1YmxpYyBvZiB0aGUpIiwgIkNvdW50cnkiXSA8LSAiRGVtb2NyYXRpY1JlcHVibGljb2Z0aGVDb25nbyIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIkNvc3RhUmljYSIsICJDb3VudHJ5Il0gPC0gIkNvc3RhIFJpY2EiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJEb21pbmljYW5SZXB1YmxpYyIsICJDb3VudHJ5Il0gPC0gIkRvbWluaWNhbiBSZXB1YmxpYyIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIkVsU2FsdmFkb3IiLCAiQ291bnRyeSJdIDwtICJFbCBTYWx2YWRvciIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIklyYW4oSXNsYW1pY1JlcHVibGljb2YpIiwgIkNvdW50cnkiXSA8LSAiSXJhbiAoSXNsYW1pYyBSZXB1YmxpYyBvZikiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJMYW9QZW9wbGUnc0RlbW9jcmF0aWNSZXB1YmxpYyIsICJDb3VudHJ5Il0gPC0gIkxhb3MiCgp3ZWlbd2VpJENvdW50cnkgPT0gIkxhbyBQZW9wbGUncyBEZW1vY3JhdGljIFJlcHVibGljIiwgIkNvdW50cnkiXSA8LSAiTGFvcyIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIlJlcHVibGljb2ZNb2xkb3ZhIiwgIkNvdW50cnkiXSA8LSAiTW9sZG92YSAoUmVwdWJsaWMgb2YpIgoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiU2llcnJhTGVvbmUiLCAiQ291bnRyeSJdIDwtICJTaWVycmEgTGVvbmUiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJTb3V0aEFmcmljYSIsICJDb3VudHJ5Il0gPC0gIlNvdXRoIEFmcmljYSIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIlNyaUxhbmthIiwgIkNvdW50cnkiXSA8LSAiU3JpIExhbmthIgoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiVGhlZm9ybWVyWXVnb3NsYXZyZXB1YmxpY29mTWFjZWRvbmlhIiwgIkNvdW50cnkiXSA8LSAiTm9ydGggTWFjZWRvbmlhIgoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiVW5pdGVkUmVwdWJsaWNvZlRhbnphbmlhIiwgIkNvdW50cnkiXSA8LSAiVGFuemFuaWEgKFVuaXRlZCBSZXB1YmxpYyBvZikiCgp3ZWlbd2VpJENvdW50cnkgPT0gIlTDvHJraXllIiwgIkNvdW50cnkiXSA8LSAiVHVya2V5IgoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiVW5pdGVkQXJhYkVtaXJhdGVzIiwgIkNvdW50cnkiXSA8LSAiVW5pdGVkIEFyYWIgRW1pcmF0ZXMiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJVbml0ZWRLaW5nZG9tb2ZHcmVhdEJyaXRhaW5hbmROb3J0aGVybklyZWxhbmQiLCAiQ291bnRyeSJdIDwtICJVbml0ZWQgS2luZ2RvbSIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIlVuaXRlZFN0YXRlc29mQW1lcmljYSIsICJDb3VudHJ5Il0gPC0gIlVuaXRlZCBTdGF0ZXMiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJWaWV0TmFtIiwgIkNvdW50cnkiXSA8LSAiVmlldCBOYW0iCgpgYGAKVGhhdCB0b29rIGEgV0hJTEUhIEJ1dCwgbm93IG15IHR3byBkYXRhc2V0cyBzaG91bGQgYmUgYmV0dGVyIG1hdGNoZWQsIGFuZCBpdCdsbCBiZSBtdWNoIGVhc2llciB0byBqb2luIHRoZW0hIAoKTm93IEknbSBnb2luZyB0byByZW1vdmUgc29tZSB1bm5lY2Vzc2FyeSB2YXJpYWJsZXMgZnJvbSB0aGUgZGF0YXNldHMgVGhpcyBpcyBiZWNhdXNlIHNvbWUgdmFyaWFibGVzIGhhdmUgdG9vIG1hbnkgbWlzc2luZyBkYXRhcG9pbnRzIHRvIGJlIHVzZWZ1bCwgb3IgYmVjYXVzZSBzb21lIHZhcmlhYmxlcyBtYXkgYmUgcmVkdW5kYW50LiAKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQoKbGVkXzIwMTUgPSBzdWJzZXQobGVkXzIwMTUsIHNlbGVjdCA9IC1jKEFsY29ob2wsIFRvdGFsZXhwZW5kaXR1cmUsWWVhcikpCgpgYGAKCk5vdywgSSB3aWxsIGJlIGpvaW5pbmcgdGhlIHR3byBkYXRhc2V0cyB1c2luZyBpbm5lcl9qb2luLCBiZWNhdXNlIEkgb25seSB3YW50IHRoZSBjb3VudHJpZXMgaW4gY29tbW9uIGJldHdlZW4gYm90aCBkYXRhZnJhbWVzLiAgSSdtIGFsc28gZ29pbmcgdG8gY3JlYXRlIGEgc2Vjb25kIHZlcnNpb24gdGhhdCBpcyBmdWxsX2pvaW5lZCwganVzdCBpbiBjYXNlIEkgZW5kIHVwIG5lZWRpbmcgaXQhCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KZGF0YXNldCA8LSB3ZWklPiUgaW5uZXJfam9pbihsZWRfMjAxNSxieT0iQ291bnRyeSIpCgpkYXRhc2V0MiA8LSB3ZWklPiUgZnVsbF9qb2luKGxlZF8yMDE1LGJ5PSJDb3VudHJ5IikKZGltKGRhdGFzZXQpCmBgYAoKRmluYWxseSwgSSB3aWxsIGJlIHJlbmFtaW5nIGEgZmV3IHZhcmlhYmxlcyBiZWNhdXNlIEkgZGlzbGlrZSBoYXZpbmcgc3BhY2VzIGluIG15IHZhcmlhYmxlIG5hbWVzLiBBZnRlciB0aGlzLCBJIHdpbGwgaGF2ZSBmaW5pc2hlZCBjbGVhbmluZyB0aGUgZGF0YSEgSSdtIGFsc28gcmVtb3ZpbmcgYSBmZXcgbW9yZSB2YXJpYWJsZXMganVzdCBiZWNhdXNlIHRoZXkgc2VlbSByYXRoZXIgcmVkdW5kYW50IC0gZm9yIGV4YW1wbGUsIHRoZSBHZW5kZXIgUGFyaXR5IEdyb3VwIGFuZCBHZW5kZXIgUGFyaXR5IEluZGV4IHZhcmlhYmxlcyBtYXkgYmUgdG9vIHNpbWlsYXIgdG8gV0VJLCB0aGVyZWZvcmUgdGhleSBtaWdodCBoYXZlIHNrZXdlZCBsZXZlbHMgb2YgY29ycmVsYXRpb24uCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KZGF0YXNldCA8LSBkYXRhc2V0ICU+JQogIHJlbmFtZShXRUkgPSAiV29tZW4ncyBFbXBvd2VybWVudCBJbmRleCAoV0VJKSAtIDIwMjIiKQoKZGF0YXNldCA8LSBkYXRhc2V0ICU+JQogIHJlbmFtZShXRUcgPSAiV29tZW4ncyBFbXBvd2VybWVudCBHcm91cCAtIDIwMjIiKQoKZGF0YXNldCA8LSBkYXRhc2V0ICU+JQogIHJlbmFtZShHR1BJID0gIkdsb2JhbCBHZW5kZXIgUGFyaXR5IEluZGV4IChHR1BJKSAtIDIwMjIiKQoKZGF0YXNldCA8LSBkYXRhc2V0ICU+JQogIHJlbmFtZShIREcgPSAiSHVtYW4gRGV2ZWxvcG1lbnQgR3JvdXAgLSAyMDIxIikKCmRhdGFzZXQgPC0gZGF0YXNldCAlPiUKICByZW5hbWUoR1BHID0gIkdlbmRlciBQYXJpdHkgR3JvdXAgLSAyMDIyIikKCmRhdGFzZXQgPC0gZGF0YXNldCAlPiUKICByZW5hbWUoU0RHX1JlZ2lvbiA9ICJTdXN0YWluYWJsZSBEZXZlbG9wbWVudCBHb2FsIHJlZ2lvbnMiKQpgYGAKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KZGF0YXNldCA9IHN1YnNldChkYXRhc2V0LCBzZWxlY3QgPSAtYyhHUEcsIFNER19SZWdpb24sR0dQSSkpCmBgYApBbGwgZmluaXNoZWQgY2xlYW5pbmchIE5vdyBJIHdhbnQgdG8gdGFrZSBhIHBlZWsgYXQgdGhlIGRhdGFzZXQgSSdsbCBiZSB3b3JraW5nIHdpdGggYmVmb3JlIEkgZ28gaW50byB0aGUgbmV4dCBzdGVwcy4gCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KZGltKGRhdGFzZXQpCnN1bW1hcnkoZGF0YXNldCRXRUkpCnRhbGx5KGRhdGFzZXQkU3RhdHVzKQpgYGAKIyBJbnRpYWwgVmlzdWFsaXphdGlvbiAKTm93LCBJIHdhbnQgdG8gY3JlYXRlIGEgbWFwIG9mIFdFSSBieSBjb3VudHJ5LCBqdXN0IHRvIGdldCBhIGdlbmVyYWwgdmlzdWFsIG9mIGhvdyB0aGUgZGlzdHJpYnV0aW9uIGxvb2tzLiAKCkknbSBnb2luZyB0byBzdGFydCBvdXQgYnkgY3JlYXRpbmcgYSBqb2luZWQgZGF0YXNldCBiZXR3ZWVuICpkYXRhc2V0KiBhbmQgKldvcmxkKiwgc28gSSBjYW4gaGF2ZSBhIG1hcHBhYmxlIGRhdGFzZXQgdXNpbmcgdGhlICoqdG1hcCoqIHBhY2thZ2UuIApgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9CmxpYnJhcnkodG1hcCkKZGF0YSgiV29ybGQiKQoKZGF0YXNldDwtIGRhdGFzZXQgJT4lCiAgcmVuYW1lKG5hbWUgPSBDb3VudHJ5KQoKd2VpLm1hcCA8LSBpbm5lcl9qb2luKGRhdGFzZXQsV29ybGQsYnk9Im5hbWUiKQoKYGBgClVuZm9ydHVuYXRlbHksIHRoZSAqd2VpLm1hcCogb25seSBjb250YWlucyA5OCBpbmRpdmlkdWFscyBkZXNwaXRlICpkYXRhc2V0KiBjb250YWluaW5nIDExMiBpbmRpdmlkdWFscyEgVGhpcyBtZWFucyB0aGF0IDE0IGluZGl2aWR1YWxzIGhhdmUgZGlzaGFybW9uaW91cyBuYW1lcyBiZXR3ZWVuICpkYXRhc2V0KiBhbmQgKldvcmxkKi4gSSdtIGdvaW5nIHRvIHJ1biBzb21lIGNvZGUgcmVuYW1pbmcgYSB3aG9sZSBidW5jaCBvZiBjb3VudHJpZXMsIGp1c3QgbGlrZSBJIGRpZCBhYm92ZSB3aXRoIHRoZSAqbGVkXzIwMTUqIGRhdGFzZXQuIEknbSBnb2luZyB0byBoaWRlIHRoaXMgY29kZSwganVzdCBiZWNhdXNlIGl0J3Mgc3VwZXIgdGVkaW91cyB3b3JrLiBUaWR5aW5nICYgY2xlYW5pbmcgeW91ciBkYXRhIGlzIGEgY29uc3RhbnQgcHJvY2VzcyEKYGBge3J9CmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJCb3NuaWEgYW5kIEhlcnplZ292aW5hIiwgIm5hbWUiXSA8LSAiQm9zbmlhIGFuZCBIZXJ6LiIKCmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJEZW1vY3JhdGljUmVwdWJsaWNvZnRoZUNvbmdvIiwgIm5hbWUiXSA8LSAiRGVtLiBSZXAuIENvbmdvIgoKZGF0YXNldFtkYXRhc2V0JG5hbWUgPT0gIkRvbWluaWNhbiBSZXB1YmxpYyIsICJuYW1lIl0gPC0gIkRvbWluaWNhbiBSZXAuIgoKZGF0YXNldFtkYXRhc2V0JG5hbWUgPT0gIklyYW4gKElzbGFtaWMgUmVwdWJsaWMgb2YpIiwgIm5hbWUiXSA8LSAiSXJhbiIKCmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJMYW9zIiwgIm5hbWUiXSA8LSAiTGFvIFBEUiIKCmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJOb3J0aCBNYWNlZG9uaWEiLCAibmFtZSJdIDwtICJNYWNlZG9uaWEiCgpkYXRhc2V0W2RhdGFzZXQkbmFtZSA9PSAiTW9sZG92YSAoUmVwdWJsaWMgb2YpIiwgIm5hbWUiXSA8LSAiTW9sZG92YSIKCmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJUYW56YW5pYSAoVW5pdGVkIFJlcHVibGljIG9mKSIsICJuYW1lIl0gPC0gIlRhbnphbmlhIgoKZGF0YXNldFtkYXRhc2V0JG5hbWUgPT0gIlZpZXQgTmFtIiwgIm5hbWUiXSA8LSAiVmlldG5hbSIKYGBgCk5vdywgbGV0J3MgdHJ5IHJlLWpvaW5pbmcgdGhlc2UgZGF0YXNldHMhCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IixtZXNzYWdlPUZBTFNFfQp3ZWkubWFwIDwtIHJpZ2h0X2pvaW4oZGF0YXNldCxXb3JsZCxieT0ibmFtZSIpCmxpYnJhcnkoczIpCmxpYnJhcnkoc2YpCndlaS5tYXAuc2YgIDwtIHdlaS5tYXAgJT4lIAogIHN0X2FzX3NmKCkKYGBgCkFuZCB2aXN1YWxpemluZzoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9Cgp0bWFwX21vZGUoInBsb3QiKQp0bV9zaGFwZSh3ZWkubWFwLnNmKSArCiAgICB0bV9wb2x5Z29ucygiV0VJIixwYWxldHRlPSJtYWdtYSIsbj04KSArCnRtX2xheW91dChpbm5lci5tYXJnaW5zID0gYyguMDUsIC4wNSwgLjA1LCAuMDUpLCBzZXBpYS5pbnRlbnNpdHk9LjAxLCBmcmFtZS5kb3VibGUubGluZT0xLCB0aXRsZSA9ICJQbG90IG9mIFdvbWVuJ3MgRW1wb3dlcm1lbnQgSW5kZXggU2NvcmVzIiwgdGl0bGUucG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgdGl0bGUuZm9udGZhY2U9ImJvbGQiLHRpdGxlLmZvbnRmYW1pbHk9IlRpbWVzIiwgbGVnZW5kLnRpdGxlLmZvbnRmYW1pbHkgPSAiVGltZXMiLGxlZ2VuZC50aXRsZS5mb250ZmFjZT0iYm9sZCIsIHRpdGxlLmJnLmNvbG9yPSJsaWdodHNhbG1vbiIsIHRpdGxlLnNpemU9MS44LCBsZWdlbmQucG9zaXRpb249YygibGVmdCIsImNlbnRlciIpLCBzYXR1cmF0aW9uID0gLjksIHRpdGxlLmJnLmFscGhhPS43KQoKYGBgCkxvdmVseSEgSSBhbHNvIG1hZGUgYW4gaW50ZXJhY3RpdmUgdmVyc2lvbiBvZiB0aGUgbWFwLCBqdXN0IGZvciBmdXJ0aGVyIGV4cGxvcmF0aW9uIG9mIHRoZSB0bWFwIHBhY2thZ2UuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KCnRtYXBfbW9kZSgidmlldyIpCnRtX3NoYXBlKHdlaS5tYXAuc2YpICsKICAgIHRtX3BvbHlnb25zKCJXRUkiLHBhbGV0dGU9Im1hZ21hIixuPTgpICsKdG1fbGF5b3V0KGlubmVyLm1hcmdpbnMgPSBjKC4wNSwgLjA1LCAuMDUsIC4wNSksIHNlcGlhLmludGVuc2l0eT0uMDEsIGZyYW1lLmRvdWJsZS5saW5lPTEsIHRpdGxlID0gIlBsb3Qgb2YgV29tZW4ncyBFbXBvd2VybWVudCBJbmRleCBTY29yZXMiLCB0aXRsZS5wb3NpdGlvbiA9IGMoInJpZ2h0IiwgImJvdHRvbSIpLCB0aXRsZS5mb250ZmFjZT0iYm9sZCIsdGl0bGUuZm9udGZhbWlseT0iVGltZXMiLCBsZWdlbmQudGl0bGUuZm9udGZhbWlseSA9ICJUaW1lcyIsbGVnZW5kLnRpdGxlLmZvbnRmYWNlPSJib2xkIiwgdGl0bGUuYmcuY29sb3I9ImxpZ2h0c2FsbW9uIiwgdGl0bGUuc2l6ZT0xLjgsIGxlZ2VuZC5wb3NpdGlvbj1jKCJsZWZ0IiwiY2VudGVyIiksIHNhdHVyYXRpb24gPSAuOSwgdGl0bGUuYmcuYWxwaGE9LjcpCgpgYGAKTm93LCBJIHdhbnQgdG8gbW92ZSBvbnRvIGNyZWF0aW5nIG1vZGVscyB0aGF0IHdpbGwgaGVscCBtZSBwcmVkaWN0IHRoZSBXRUkgc2NvcmVzIG9mIGNvdW50cmllcyBiYXNlZCBvbiBvdGhlciBmYWN0b3JzLgoKIyBEZWNpc2lvbiBUcmVlCkknbSBnb2luZyB0byBzdGFydCBieSBjcmVhdGluZyBhIGRlY2lzaW9uIHRyZWUgZm9yIG15ICpkYXRhc2V0Ki4gSSBiZWxpZXZlIHRoaXMgd2lsbCBiZSBiZW5lZmljaWFsIHRvIHNlZSwgc2VlaW5nIGFzIHRoZSBkZWNpc2lvbiB0cmVlIHdpbGwgc2VsZWN0IHRoZSBiZXN0IHByZWRpY3RvcnMgZm9yIG15IHJlc3BvbnNlIHZhcmlhYmxlLiBUaGUgZmlyc3Qgc3RlcCBoZXJlIGlzIHRvIHNwbGl0IG15IGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBkYXRhLgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsbWVzc2FnZT1GQUxTRX0KCnNldC5zZWVkKDIyOCkKdHJhaW4uaW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihkYXRhc2V0JFdFSSwgcD0gMC44LCBsaXN0ID0gRkFMU0UpCnRyYWluIDwtIGRhdGFzZXRbdHJhaW4uaW5kZXgsXQp0ZXN0IDwtIGRhdGFzZXRbLXRyYWluLmluZGV4LF0KYGBgCkknbSBnb2luZyB0byBsb29rIGF0IHRoZSBwcm9wb3J0aW9uIG9mIGRldmVsb3BlZCB2cyBkZXZlbG9waW5nIGNvdW50cmllcyB3aXRoaW4gYm90aCBteSAqdGVzdCogYW5kICp0cmFpbiogZGF0YS4gVGhpcyBpcyBzbyBJIGNhbiBlbnN1cmUgdGhhdCAqdGVzdCogaXMgYXQgbGVhc3Qgc29tZXdoYXQgcmVwcmVzZW50YXRpdmUgb2YgdGhlIGRpc3RyaWJ1dGlvbiB3aXRoaW4gKnRyYWluKi4KYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLG1lc3NhZ2U9RkFMU0V9CnRhbGx5KHRlc3QkU3RhdHVzLCBmb3JtYXQ9J3Byb3BvcnRpb24nKQp0YWxseSh0cmFpbiRTdGF0dXMsIGZvcm1hdCA9ICdwcm9wb3J0aW9uJykKdGFsbHkoZGF0YXNldCRTdGF0dXMsIGZvcm1hdCA9ICdwcm9wb3J0aW9uJykKYGBgCkJvdGggKnRlc3QqIGFuZCAqdHJhaW4qIGFwcGVhciB0byBiZSBhZGVxdWF0ZWx5IHJlcHJlc2VudGF0aXZlIG9mICpkYXRhc2V0KiEKCk5vdywgSSB3aWxsIGNyZWF0ZSBteSBkZWNpc2lvbiB0cmVlLiBJIHdpbGwgZml0IHRoZSBkZWNpc2lvbiB0cmVlIHRvICp0cmFpbiosIGFuZCBJIHdpbGwgc2V0IG5lYXJseSBldmVyeSBwcmVkaWN0b3IgdmFyaWFibGUgKEJFU0lERVMgTkFNRSAmIFdFRywgZm9yIHJlYXNvbnMgb2YgcmVkdW5kYW5jeSkgYXMgbXkgWC4gSSBhbSBkb2luZyB0aGlzIHNvIHRoYXQgdGhlIGRlY2lzaW9uIHRyZWUgY2FuIHRlbGwgbWUgd2hpY2ggcHJlZGljdG9yIHZhcmlhYmxlcyBoYXZlIHRoZSBoaWdoZXN0IGluZmx1ZW5jZSBvbiBteSByZXNwb25zZSB2YXJpYWJsZSwgV0VJLgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsbWVzc2FnZT1GQUxTRX0KdHJlZSA8LSBycGFydChXRUl+IEhERyArIFN0YXR1cyArIExpZmVleHBlY3RhbmN5ICsgQWR1bHRNb3J0YWxpdHkgKyBpbmZhbnRkZWF0aHMgKyBIZXBhdGl0aXNCICsgTWVhc2xlcyArICBQb2xpbyArIERpcGh0aGVyaWEgKyBQb3B1bGF0aW9uICsgU2Nob29saW5nLCBkYXRhPXRyYWluLCBjcD0uMDEpCgpycGFydC5wbG90KHRyZWUpCmBgYAoKSXQgYXBwZWFycyB0aGVyZSBhcmUgdGhyZWUgcHJlZGljdG9ycyB0aGF0IGhhdmUgdGhlIGhpZ2hlc3QgY29ycmVsYXRpb24gd2l0aCBXRUkgc2NvcmVzIDogSERHIChIdW1hbiBEZXZlbG9wbWVudCBHcm91cCksIFllYXJzIG9mIFNjaG9vbGluZywgYW5kIEFkdWx0IE1vcnRhbGl0eSBSYXRlcy4gRm9yIGZ1cnRoZXIgZXhwbG9yYXRpb24sIEknbSBnb2luZyB0byBjcmVhdGUgYSBSYW5kb20gRm9yZXN0IHRvIGZpbmQgb3VyIGJlc3QgbW9kZWwuCgojIFJhbmRvbSBGb3Jlc3QgJiBSTVNFCkknbSBnb2luZyB0byBoYXZlIHRvIG5vdCB1c2UgYW55IHZhcmlhYmxlcyB0aGF0IGhhdmUgIk5BIiB2YWx1ZXMgdG8gYmUgYWJsZSB0byBwcm9wZXJseSBjcmVhcnRlIGEgcmFuZG9tIGZvcmVzdC4gVGhpcyB1bmZvcnR1bmF0ZWx5IG1lYW5zIHJlbW92aW5nIFNjaG9vbGluZy4gSG93ZXZlciwgSSBkb24ndCB3YW50IHRvIHJlbW92ZSBTY2hvb2xpbmcgYmVjYXVzZSBhY2NvcmRpbmcgdG8gdGhlIGRlY2lzaW9uIHRyZWUsIGl0J3Mgb25lIG9mIHRoZSBtb3N0IGltcG9ydGFudCBwcmVkaWN0b3JzISBTbywgSSBhbSBnb2luZyB0byB1c2UgcmVncmVzc2lvbiBpbXB1dGF0aW9uIHRvIGNyZWF0ZSBzb21lIGVzdGltYXRlZCBTY2hvb2xpbmcgdmFsdWVzIGZvciBhbnkgb2Ygb3VyIGNvdW50cmllcyB0aGF0IGhhdmUgU2Nob29saW5nID0gTkEuIApgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIG1lc3NhZ2U9RkFMU0V9Cm1pc3NpbmcgPC0gaXMubmEoZGF0YXNldCRTY2hvb2xpbmcpIAoKc3VtKG1pc3NpbmcpCgp3aGljaChtaXNzaW5nKQoKbG0oU2Nob29saW5nfldFSSwgZGF0YT1kYXRhc2V0KQoKCmRhdGFzZXQkU2Nob29saW5nWzExXTwtMi41NzQrMTcuOTQzKigwLjcwNykKZGF0YXNldCRTY2hvb2xpbmdbMzBdPC0yLjU3NCsxNy45NDMqKDAuNzc4KQpkYXRhc2V0JFNjaG9vbGluZ1szMV08LTIuNTc0KzE3Ljk0MyooMC43NTIpCmRhdGFzZXQkU2Nob29saW5nWzQ3XTwtMi41NzQrMTcuOTQzKigwLjY4NikKZGF0YXNldCRTY2hvb2xpbmdbNzJdPC0yLjU3NCsxNy45NDMqKDAuMzk5KQpkYXRhc2V0JFNjaG9vbGluZ1sxMDVdPC0yLjU3NCsxNy45NDMqKDAuNTEwKQpgYGAKWWV0IGFub3RoZXIgZXhhbXBsZSBvZiBuZXZlciBiZWluZyBkb25lIHdpdGggY2xlYW5pbmcgdGhlIGRhdGEhIE5vdywgSSBoYXZlIHRvIHJlY3JlYXRlIG15IHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGFzZXQgd2l0aG91dCB0aGVzZSBtaXNzaW5nIHZhbHVlcy4gCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgbWVzc2FnZT1GQUxTRX0Kc2V0LnNlZWQoMjI4KQp0cmFpbi5pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGRhdGFzZXQkV0VJLCBwPSAwLjgsIGxpc3QgPSBGQUxTRSkKdHJhaW4gPC0gZGF0YXNldFt0cmFpbi5pbmRleCxdCnRlc3QgPC0gZGF0YXNldFstdHJhaW4uaW5kZXgsXQpgYGAKClRoZW4sIEkgY2FuIGZpbmFsbHkgY3JlYXRlIG15IFJhbmRvbSBGb3Jlc3QKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLG1lc3NhZ2U9RkFMU0V9CnNldC5zZWVkKDIyOCkKcmZfbW9kZWwgPC1yYW5kb21Gb3Jlc3QoV0VJIH4gSERHICsgU3RhdHVzICsgTGlmZWV4cGVjdGFuY3kgKyBTY2hvb2xpbmcgKyBBZHVsdE1vcnRhbGl0eSArIGluZmFudGRlYXRocyArIE1lYXNsZXMgKyAgUG9saW8gKyBEaXBodGhlcmlhICxkYXRhPXRyYWluLCBwcm94aW1pdHk9VFJVRSwgbnRyZWU9MTAwMCkKcmZfbW9kZWwKCmBgYApUaGUgbWVhbiBvZiBzcXVhcmVkIHJlc2lkdWFscyBpcyBwcmV0dHkgY2xvc2UgdG8gMCwgd2hpY2ggbWVhbnMgdGhlIG1vZGVsIGlzIGdvb2QhIEhvd2V2ZXIsIEknbSBnb2luZyB0byBhbHNvIHRyeSBydW5uaW5nIGEgUmFuZG9tIEZvcmVzdCBtb2RlbCBpbiB3aGljaCBJIG9ubHkgdXNlIHRoZSB0aHJlZSBiZXN0IHByZWRpY3RvciB2YXJpYWJsZXMgYXMgaWRlbnRpZmllZCBieSBteSBEZWNpc2lvbiBUcmVlLiBJIHdpbGwgdGhlbiBjb21wYXJlIHRoZSB0d28gbWVhbiBvZiBzcXVhcmVkIHJlc2lkdWFscy4KYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLG1lc3NhZ2U9RkFMU0V9CnNldC5zZWVkKDIyOCkKcmZfbW9kZWwyIDwtcmFuZG9tRm9yZXN0KFdFSSB+IEhERyArICBTY2hvb2xpbmcgKyBBZHVsdE1vcnRhbGl0eSAgLGRhdGE9dHJhaW4sIHByb3hpbWl0eT1UUlVFLCBudHJlZT0xMDAwKQpyZl9tb2RlbDIKCmBgYApUaGUgbWVhbiBvZiBzcXVhcmVkIHJlc2lkdWFscyBpcyBldmVyIHNvIHNsaWdodGx5IGhpZ2hlciBmb3IgdGhpcyBtb2RlbCwgYW5kIHRoZSBWYXIgZXhwbGFpbmVkIGlzIHNsaWdodGx5IGxvd2VyLiBJJ20gZ29pbmcgdG8gdXNlIGFuIFJNU0UgdGVzdCB0byBjb21wYXJlIHRoZSB0d28gbW9kZWxzLgoKCk5vdywgSSdtIGdvaW5nIHRvIGJvb3N0IHRoZSB0d28gbW9kZWxzLiBCZWZvcmUgZG9pbmcgc28sIEkgYW0gY2hhbmdpbmcgSERHJ3MgY2xhc3MgdG8gb3JkZXJlZCwgYW5kIFN0YXR1cydzIGNsYXNzIHRvIG9yZGVyZWQsIHNvIHRoZXkgYXJlY29tcGF0aWJsZSB3aXRoIHRoZSBib29zdGVkIG1vZGVsLgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsbWVzc2FnZT1GQUxTRX0KdHJhaW4kSERHIDwtIGFzLm9yZGVyZWQodHJhaW4kSERHKQp0cmFpbiRTdGF0dXMgPC0gYXMub3JkZXJlZCh0cmFpbiRTdGF0dXMpCmJvb3N0MSA8LSBnYm0oV0VJIH4gSERHICsgU3RhdHVzICsgTGlmZWV4cGVjdGFuY3kgKyBTY2hvb2xpbmcgKyBBZHVsdE1vcnRhbGl0eSArIGluZmFudGRlYXRocyArIE1lYXNsZXMgKyAgUG9saW8gKyBEaXBodGhlcmlhLCBkYXRhPXRyYWluLGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsIG4udHJlZXMgPSAxMDAwLCBzaHJpbmthZ2UgPSAwLjAxLCBuLm1pbm9ic2lubm9kZT01KQpib29zdDIgPC0gZ2JtKFdFSX4gU2Nob29saW5nICsgSERHICsgQWR1bHRNb3J0YWxpdHkgLCBkYXRhID0gdHJhaW4sIGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsIG4udHJlZXMgPSAxMDAwLCBzaHJpbmthZ2UgPSAwLjAxLCBuLm1pbm9ic2lubm9kZT01KQoKYGBgCk5vdyBJIHdpbGwgZXZhbHVhdGUgdGhlIHR3byBtb2RlbHMgdXNpbmcgcHJlZGljdGlvbnMgJiBSTVNFLgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KcHJlZGljdGlvbnMxIDwtIHByZWRpY3QoYm9vc3QxLG5ld2RhdGE9dGVzdCkKcm1zZShhY3R1YWw9dGVzdCRXRUksIHByZWRpY3RlZD1wcmVkaWN0aW9uczEpCgpwcmVkaWN0aW9uczIgPC0gcHJlZGljdChib29zdDIsIG5ld2RhdGEgPSB0ZXN0KQoJcm1zZShhY3R1YWw9dGVzdCRXRUksIHByZWRpY3RlZD1wcmVkaWN0aW9uczIpCmBgYApPbmNlIGFnYWluLCB0aGUgbW9kZWwgd2l0aCBuZWFybHkgdmVyeSB2YXJpYWJsZSBoYXMgcHJvdmVkIHRvIHBlcmZvcm0gKnNsaWdodGx5KiBiZXR0ZXIgdGhhbiB0aGUgbW9kZWwgd2l0aCBvbmx5IDMgcHJlZGljdG9yIHZhcmlhYmxlcy4gSG93ZXZlciwgdGhpcyBkaWZmZXJlbmNlIGlzICoqc28gc21hbGwqKiwgYW5kIEkgdGhpbmsgaXQgaXMgd29ydGggaXQgdG8gc2FjcmlmaWNlIHRoaXMgc21hbGwgZGlmZmVyZW5jZSBmb3IgdGhlIHNha2Ugb2Yga2VlcGluZyBvdXIgbW9kZWwgU0lNUExFISBUaGUgbW9kZWwgc3RpbGwgcGVyZm9ybXMgdmVyeSB3ZWxsLiBTbywgSSdtIGRlY2lkaW5nIHRvIGtlZXAgdGhlIG1vZGVsIHRoYXQgb25seSBjb250YWlucyB0aGUgdGhyZWUgYmVzdCBwcmVkaWN0b3IgdmFyaWFibGVzISAKCiMgRmluYWwgVmlzdWFsaXphdGlvbnMKYGBge3IsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0Kd2VpLm1hcCA8LSByaWdodF9qb2luKGRhdGFzZXQsV29ybGQsYnk9Im5hbWUiKQoKd2VpLm1hcC5zZiAgPC0gd2VpLm1hcCAlPiUgCiAgc3RfYXNfc2YoKQpgYGAKSSdtIGdvaW5nIHRvIGNyZWF0ZSBGT1VSIG1hcHMgaGVyZTogT25lIGV4YWN0bHkgbGlrZSBteSBmaXJzdCB2aXN1YWxpemF0aW9uIHRoYXQgc2hvd3MgV0VJIHNjb3JlcyBieSBjb3VudHJ5LCBhbmQgdGhlbiB0aHJlZSB0aGF0IHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgZWFjaCBvZiBteSBwcmVkaWN0b3IgdmFyaWFibGVzISBJIHdpbGwgZGlzcGxheSBhbGwgb2YgdGhlc2UgbWFwcyBzaWRlIGJ5IHNpZGUuCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQp0bWFwX21vZGUoInBsb3QiKQoKIHRtX3NoYXBlKHdlaS5tYXAuc2YpICsgdG1fcG9seWdvbnMoIldFSSIscGFsZXR0ZT0ibWFnbWEiLG49OCkgKwp0bV9sYXlvdXQoaW5uZXIubWFyZ2lucyA9IGMoLjA1LCAuMDUsIC4wNSwgLjA1KSwgc2VwaWEuaW50ZW5zaXR5PS4wMSwgZnJhbWUuZG91YmxlLmxpbmU9MSwgdGl0bGUgPSAiUGxvdCBvZiBXb21lbidzIEVtcG93ZXJtZW50IEluZGV4IFNjb3JlcyIsIHRpdGxlLnBvc2l0aW9uID0gYygicmlnaHQiLCAiYm90dG9tIiksIHRpdGxlLmZvbnRmYWNlPSJib2xkIix0aXRsZS5mb250ZmFtaWx5PSJUaW1lcyIsIGxlZ2VuZC50aXRsZS5mb250ZmFtaWx5ID0gIlRpbWVzIixsZWdlbmQudGl0bGUuZm9udGZhY2U9ImJvbGQiLCB0aXRsZS5iZy5jb2xvcj0ibGlnaHRzYWxtb24iLCB0aXRsZS5zaXplPTEuOCwgbGVnZW5kLnBvc2l0aW9uPWMoImxlZnQiLCJjZW50ZXIiKSwgc2F0dXJhdGlvbiA9IC45LCB0aXRsZS5iZy5hbHBoYT0uNykKCnRtX3NoYXBlKHdlaS5tYXAuc2YpICsgdG1fcG9seWdvbnMoIkhERyIscGFsZXR0ZT0ibWFnbWEiLG49OCkgKwp0bV9sYXlvdXQoaW5uZXIubWFyZ2lucyA9IGMoLjA1LCAuMDUsIC4wNSwgLjA1KSwgc2VwaWEuaW50ZW5zaXR5PS4wMSwgZnJhbWUuZG91YmxlLmxpbmU9MSwgdGl0bGUgPSAiUGxvdCBvZiBIdW1hbiBEZXZlbG9wbWVudCBHcm91cHMiLCB0aXRsZS5wb3NpdGlvbiA9IGMoInJpZ2h0IiwgImJvdHRvbSIpLCB0aXRsZS5mb250ZmFjZT0iYm9sZCIsdGl0bGUuZm9udGZhbWlseT0iVGltZXMiLCBsZWdlbmQudGl0bGUuZm9udGZhbWlseSA9ICJUaW1lcyIsbGVnZW5kLnRpdGxlLmZvbnRmYWNlPSJib2xkIiwgdGl0bGUuYmcuY29sb3I9ImxpZ2h0c2FsbW9uIiwgdGl0bGUuc2l6ZT0xLjgsIGxlZ2VuZC5wb3NpdGlvbj1jKCJsZWZ0IiwiY2VudGVyIiksIHNhdHVyYXRpb24gPSAuOSwgdGl0bGUuYmcuYWxwaGE9LjcpCgp0bV9zaGFwZSh3ZWkubWFwLnNmKSArIHRtX3BvbHlnb25zKCJTY2hvb2xpbmciLHBhbGV0dGU9Im1hZ21hIixuPTgpICsKdG1fbGF5b3V0KGlubmVyLm1hcmdpbnMgPSBjKC4wNSwgLjA1LCAuMDUsIC4wNSksIHNlcGlhLmludGVuc2l0eT0uMDEsIGZyYW1lLmRvdWJsZS5saW5lPTEsIHRpdGxlID0gIlBsb3Qgb2YgWWVhcnMgb2YgU2Nob29saW5nIiwgdGl0bGUucG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgdGl0bGUuZm9udGZhY2U9ImJvbGQiLHRpdGxlLmZvbnRmYW1pbHk9IlRpbWVzIiwgbGVnZW5kLnRpdGxlLmZvbnRmYW1pbHkgPSAiVGltZXMiLGxlZ2VuZC50aXRsZS5mb250ZmFjZT0iYm9sZCIsIHRpdGxlLmJnLmNvbG9yPSJsaWdodHNhbG1vbiIsIHRpdGxlLnNpemU9MS44LCBsZWdlbmQucG9zaXRpb249YygibGVmdCIsImNlbnRlciIpLCBzYXR1cmF0aW9uID0gLjksIHRpdGxlLmJnLmFscGhhPS43KQoKdG1fc2hhcGUod2VpLm1hcC5zZikgKyB0bV9wb2x5Z29ucygiQWR1bHRNb3J0YWxpdHkiLHBhbGV0dGU9Im1hZ21hIixuPTgpICsKdG1fbGF5b3V0KGlubmVyLm1hcmdpbnMgPSBjKC4wNSwgLjA1LCAuMDUsIC4wNSksIHNlcGlhLmludGVuc2l0eT0uMDEsIGZyYW1lLmRvdWJsZS5saW5lPTEsIHRpdGxlID0gIkFkdWx0IE1vcnRhbGl0eSBwZXIgUG9wdWxhdGlvbiBvZiAxMDAwIiwgdGl0bGUucG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgdGl0bGUuZm9udGZhY2U9ImJvbGQiLHRpdGxlLmZvbnRmYW1pbHk9IlRpbWVzIiwgbGVnZW5kLnRpdGxlLmZvbnRmYW1pbHkgPSAiVGltZXMiLGxlZ2VuZC50aXRsZS5mb250ZmFjZT0iYm9sZCIsIHRpdGxlLmJnLmNvbG9yPSJsaWdodHNhbG1vbiIsIHRpdGxlLnNpemU9MS44LCBsZWdlbmQucG9zaXRpb249YygibGVmdCIsImNlbnRlciIpLCBzYXR1cmF0aW9uID0gLjksIHRpdGxlLmJnLmFscGhhPS43KQpgYGAKClRoZXNlIGFyZSBzb3J0IG9mIGNvbmZ1c2luZy4gRm9yIG9uZSwgdGhlIEhERyBjYXRlZ29yaWVzIGFyZSBvdXQgb2Ygb3JkZXIsIHdoaWNoIGlzIHZlcnkgaGFyZCB0byByZWFkLiBTZWNvbmRseSwgdGhlIGFkdWx0IG1vcnRhbGl0eSBtYXAgc2hvdWxkIHByb2JhYmx5IGhhdmUgdGhlIGNvbG9ycyBmbGlwcGVkLCBiZWNhdXNlIGxvd2VyIGFkdWx0IG1vcnRhbGl0eSBjb3JyZXNwb25kcyB0byAqaGlnaGVyKiBXRUkuCgpJJ20gZ29pbmcgdG8gZml4IHRoZW0sIGFuZCBpbnNlcnQgdGhlIHBuZyBiZWxvdy4KCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiL1VzZXJzL2phc21pbmVkYXkvRG93bmxvYWRzL1dFSVBMT1QuanBnIikKYGBgCgogQW5kIHRoZXJlIHdlIGhhdmUgb3VyIGZpbmFsIHZpc3VhbGl6YXRpb24hICBJIGhvcGUgeW91IGVuam95ZWQgcmVhZGluZyB0aHJvdWdoIHRoaXMgcHJvamVjdCBhcyBtdWNoIGFzIEkgZW5qb3llZCBteSBTVEFUMjI4IGNsYXNzIDopCiAKIyBCT05VUzogVGVzdGluZyB0aGUgUHJlZGljdGlvbiBNb2RlbApBZnRlciBmaW5pc2hpbmcgdGhlIGZpcnN0IGRyYWZ0IG9mIG15IHByb2plY3QsIEkgZmlndXJlZCBJIHdhbnRlZCB0byBhZGQgYSBzZWN0aW9uIGluIHdoaWNoIEkgdGFrZSBhIHNtYWxsIGhhbmRmdWwgb2YgY291bnRyaWVzIHRoYXQgYXJlIE5PVCByZXByZXNlbnRlZCBpbiB0aGUgKndlaSogZGF0YXNldCBhbmQgdXNlIG91ciBtb2RlbCB0byBwcmVkaWN0IHRoZSBXRUkgc2NvcmVzIGZvciB0aGVzZSBuYXRpb25zLiBJIHdpbGwgYmUgdXNpbmcgdGhlIGNvdW50cmllcyBLYXpha2hzdGFuLCBOZXcgWmVhbGFuZCwgU3VkYW4sIEhhaXRpLCBhbmQgVWtyYWluZS4gSSB3aWxsIHVzZSAqbGVkXzIwMTUqIHRvIHNvdXJjZSB0aGUgSERHLCBTY2hvb2xpbmcsIGFuZCBBZHVsdE1vcnRhbGl0eSBmb3IgZWFjaCBvZiB0aGVzZSBjb3VudHJpZXMuCgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KZmluYWxtb2RlbCA8LSBsbShXRUl+SERHK1NjaG9vbGluZytBZHVsdE1vcnRhbGl0eSxkYXRhPWRhdGFzZXQpCmZpbmFsbW9kZWwKI0themFraHN0YW46CiAgI0hERz1WZXJ5IGhpZ2gKICAjU2Nob29saW5nPTE1CiAgI0FkdWx0TW9ydGFsaXR5PTE5OApLYXpha2hzdGFuX1dFSSA8LSAuMDE4NiArIC4wNzE3ICsgLjAyODIqMTUgKyAuMDAwMioxOTgKI05ldyBaZWFsYW5kICAKICAjSERHPVZlcnkgaGlnaAogICNTY2hvb2xpbmc9MTkuMgogICNBZHVsdE1vcnRhbGl0eT02NgpOWl9XRUkgPC0gLjAxODYgKyAuMDcxNyArIC4wMjgyKjE5LjIgKyAuMDAwMio2NgojU3VkYW4KICAjSERHPUxvdwogICNTY2hvb2xpbmc9Ny4yCiAgI0FkdWx0TW9ydGFsaXR5PTIyNQpTdWRhbl9XRUkgPC0gLjAxODYgLSAuMDc0OSArIC4wMjgyKjcuMiArIC4wMDAyKjIyNQojSGFpdGkKICAjSERHPU1lZGl1bQogICNTY2hvb2xpbmc9OS4xCiAgI0FkdWx0TW9ydGFsaXR5PTI0CkhhaXRpX1dFSSA8LSAuMDE4NiAtIC4wMjAyICsgLjAyODIqOS4xICsgLjAwMDIqMjQKI1VrcmFpbmUKICAjSERHPUhpZ2gKICAjU2Nob29saW5nPTE1LjMKICAjQWR1bHRNb3J0YWxpdHk9MTk1ClVrcmFpbmVfV0VJIDwtIC4wMTg2ICsgLjAyODIqMTUuMyArIC4wMDAyKjE5NQpgYGAKCkthemFraHN0YW4gaW1wdXRlZC9wcmVkaWN0ZWQgV0VJIHNjb3JlID0gLjU1MwpOZXcgWmVhbGFuZCBpbXB1dGVkL3ByZWRpY3RlZCBXRUkgc2NvcmUgPSAuNjQ1ClN1ZGFuIGltcHV0ZWQvcHJlZGljdGVkIFdFSSBzY29yZSA9IC4xOTIKSGFpdGkgaW1wdXRlZC9wcmVkaWN0ZWQgV0VJIHNjb3JlID0gLjI2MApVa3JhaW5lIGltcHV0ZWQvcHJlZGljdGVkIFdFSSBzY29yZSA9IC40ODkKClRoZXNlIGRvbid0IGZlZWwgZW50aXJlbHkgYWNjdXJhdGUuIEZvciBleGFtcGxlLCBTdWRhbidzIGltcHV0ZWQgV0VJIHNjb3JlIGlzIGp1c3QgYWJvdmUgdGhhdCBvZiBZZW1lbidzLCB3aGljaCBpcyB0aGUgbG93ZXN0IGluIHRoZSB3b3JsZC4gSG93ZXZlciwgaXQgc3RpbGwgY2FuIGJlIGJlbmVmaWNpYWwgdG8gc2VlIHdoZXJlIHRoZXJlIG1heSBiZSBwb3RlbnRpYWwgZmxhd3MgaW4gdXNpbmcgdGhpcyBsaW5lYXIgbW9kZWwgdG8gcHJlZGljdCBXRUkgc2NvcmVzISBXRUkgc2NvcmUgZGF0YSBvbiAqYWxsKiBjb3VudHJpZXMgaXMgbm90IHB1YmxpYywgc28gSSBhbSB1bmZvcnR1bmF0ZWx5IHVuYWJsZSB0byBjaGVjayB0aGUgYWN0dWFsIGFjY3VyYWN5IG9mIHRoZXNlIHByZWRpY3Rpb25zLiAK